﻿#include "../../includes/scripts/cjsmanager.h"
#include "../../includes/QsLog/QsLog.h"
#include "../../includes/common/common.h"
#include "../../includes/common/fastlz.h"
#include "../../includes/common/NedAllocatorImpl.h"
#include "../../includes/network/cequipmentmanagerserver.h"

#include <QFileInfo>
#include <QDir>
#include <QTextCodec>
#include <QDebug>
#include <QQmlEngine>
#include <QJsonArray>
#include <QScreen>
#include <QtConcurrent>
#include <QApplication>
#include <QDesktopWidget>

#if defined (WIN32)
#include <QtWin>
#endif

#include <QPainter>

#define IDD_RESOURCE_UPDATE  0
#define IDD_RESOURCE_DELETE  1
#define IDD_RESOURCE_ADD     2

typedef tagpluginItem* (*reg_fun)(CJsManager *g_JsManager);

initialiseSingleton(CJsManager);

CJsManager::CJsManager(QWidget *parent)
    : m_isAutoLoadScript(false),
      m_DeviceID(0),
      m_UserID(0),
      m_DataVersion(0),
      m_Encryptedread(false),
      m_ReverseProxyServerPort(-1),
      m_ControlDevicePort(-1),
      m_recvDataState(false),
      m_SendScreenCount(-1),
      m_ScreenWidth(800),
      m_ScreenHeight(600),
      m_isEnableScreen(true),
      m_refreshfrequency(100),
      m_isDebugMode(false)
{
    //QTextCodec *codec = QTextCodec::codecForName("UTF-8");//或者"GBK",不分大小写
    //QTextCodec::setCodecForLocale(codec);

    //m_JSEngine.setParent(this);

    //m_SystemWorkingPathTimer = new QTimer(this);
    m_SystemWorkingPathTimer.setSingleShot(true);

    m_WebSocketServer.SetNetworkFrameManager(this);

    //connect(&m_JSEngine, SIGNAL(warnings(const QList<QQmlError>)), this, SLOT(jsenginewarnings(const QList<QQmlError>)));
    connect(&m_SystemWorkingPathTimer, SIGNAL(timeout()), this, SLOT(WorkingdirectoryChanged()));
    connect(&m_pSystemWorkingPathWatcher, SIGNAL(fileChanged(QString)), this, SLOT(WorkingdirectoryChange(QString)));
    connect(&m_pSystemWorkingPathWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(WorkingdirectoryChange(QString)));
    connect(&m_TimerSendScreen, SIGNAL(timeout()), this, SLOT(HandlerTimerSendScreen()));
    connect(this, SIGNAL(signal_convertImageToBytes(QByteArray)),
                     this,
            SLOT(signalProcessSendScreenToControlDevice(QByteArray)),
            Qt::QueuedConnection);

    m_ReverseProxyServer.bind(QHostAddress::Any);
    connect(&m_ReverseProxyServer,SIGNAL(readyRead()),this,SLOT(ReverseProxyServer_reciverPendingDatagram()));
}

CJsManager::~CJsManager()
{   
    clean_system();
    //cleanallplugin();
}

void CJsManager::WorkingdirectoryChange(const QString &path)
{
    m_SystemWorkingPathTimer.start(2000);
}

/*void CJsManager::jsenginewarnings(const QList<QQmlError> &warnings)
{
    emit on_signaljsenginewarnings(warnings);

    for(int i=0;i<warnings.size();i++)
    {
        QLOG_WARN()<<warnings[i].toString();
    }
}*/

/**
 * @brief CJsManager::startDebugServer 启动调试服务器
 * @param serverport
 * @return
 */
bool CJsManager::startDebugServer(int serverport)
{
    if(m_WebSocketServer.isListening())
        return false;

    m_DebugServerIPPort = serverport;

    m_WebSocketServer.OpenServer(m_DebugServerIPPort);
}

/**
 * @brief CJsManager::closeDebugServer 关闭调试服务器
 */
void CJsManager::closeDebugServer(void)
{
    m_WebSocketServer.CloseServer();
}

/**
 * @brief CJsManager::isstardedDebugServer 检测是否启动调试服务器
 * @return
 */
bool CJsManager::isstardedDebugServer(void)
{
    return m_WebSocketServer.isListening();
}

/**
 * @brief CJsManager::init_system 初始化系统
 * @param workingpath 系统工作目录
 * @param regdllpath 插件目录
 * @param debugserverport 要连接的调试服务器端口
 *
 * @return 如果系统初始化成功返回真，否则返回假
 */
bool CJsManager::init_system(QString workingpath,QString regdllpath,
                             int debugserverport,
                             bool isauotoloadscript,
                             bool isConnectServer,
                             bool isDebugMode)
{
    if(workingpath.isEmpty())
        return false;

    //this->clean_system();
    this->cleanallplugin();

    // 设置工作目录
    this->setCurrentWorkingPath(workingpath);

    // 设置插件目录
    this->setRegisterDllPath(regdllpath);

    // 设置是否自动导入脚本
    this->setAutoLoadScript(true);

    // 设置是否使用调试
    this->setEnableDebug(isDebugMode);

    // 是否启用网络
    if(isConnectServer && debugserverport > 0)
    {
        m_DebugServerIPPort = debugserverport;

        m_WebSocketServer.OpenServer(m_DebugServerIPPort);
    }

    RegisterNewObject("sys",this,true);

    // 导入注册的插件
    reg_all2jss();

    // 导入脚本文件
    if(isauotoloadscript && js_loadscriptstring_callback(m_currentWorkingPath))
        js_setup_callback();

    QString tmpMsg2 = QString::fromLocal8Bit(" <b>启动调试服务器，端口:</b>")+QString::asprintf("%d",debugserverport);

    QString logmsg =
            QString::fromLocal8Bit("脚本系统初始成功:")+
            QString::fromLocal8Bit(" <b>工作目录:</b>")+workingpath+
            QString::fromLocal8Bit(" <b>插件目录:</b>")+regdllpath+
            (isConnectServer ? tmpMsg2 : "")+
            QString::fromLocal8Bit(" <b>是否启用调试:</b>")+QString::asprintf("%d",(int)m_isDebugMode)+
            QString::fromLocal8Bit(" <b>自动加载脚本:</b>")+QString::asprintf("%d",isauotoloadscript);
    Log(logmsg,"info",false);

    return true;
}

/**
 * @brief CJsManager::RegisterNewObject 注册一个新的对象
 * @param objName 新对象的名称
 * @param newobject 要注册的对象
 * @param isCppOwnership 如果设置成真表示归c++所有，如果设置成假表示归JS所有，JS在退出时会自动删除这个对象
 * @return 返回注册的值
 */
QJSValue CJsManager::RegisterNewObject(QString objName,QObject *newobject,bool isCppOwnership)
{
    if(isCppOwnership)
        QQmlEngine::setObjectOwnership(newobject, QQmlEngine::CppOwnership);

    QJSValue returnObj = m_JSEngine.newQObject(newobject);
    if (returnObj.isError())
    {
        QString logmsg = "RegisterNewObject error:";
        logmsg += returnObj.property("name").toString() + ", "
                 + returnObj.property("message").toString() + ", "
                 //+ errorValue.property("lineNumber").toInt() + ", "
                 + returnObj.property("stack").toString();
        Log(logmsg,"error",false);

        return returnObj;
    }

    /*QString logmsg = "RegisterNewObject successed:" +
            objName +
            " " +
            QString::asprintf("%d",isCppOwnership);
    Log(logmsg,"info");*/

    m_JSEngine.globalObject().setProperty(objName, returnObj);

    return returnObj;
}

/**
 * @brief CJsManager::RegisterNewClass 注册一个新的类
 * @param className 类名
 * @param newmetaObject 要注册的类
 * @return
 *
 * class MyObject : public QObject{
 *      Q_OBJECT
 *   public:
 *       Q_INVOKABLE MyObject() {}
 *       Q_INVOKABLE bool hasChannel(int id);
 *   };
 *   QJSEngine engine;
 *       QJSValue injectedObject;
 *       injectedObject= engine.newQMetaObject(&MyObject::staticMetaObject);
 *       engine.globalObject().setProperty("MyObject", injectedObject);
 *
 *  let myObject = new MyObject();
 *  myObject.hasChannel(1234);
 */
QJSValue CJsManager::RegisterNewClass(QString className,const QMetaObject *newmetaObject)
{
    QJSValue injectedObject = m_JSEngine.newQMetaObject(newmetaObject);

    if (injectedObject.isError())
    {
        QString logmsg = "RegisterNewClass error:";
        logmsg += injectedObject.property("name").toString() + ", "
                 + injectedObject.property("message").toString() + ", "
                 //+ errorValue.property("lineNumber").toInt() + ", "
                 + injectedObject.property("stack").toString();
        Log(logmsg,"error",false);

        return injectedObject;
    }

    //QString logmsg = "RegisterNewClass successed:" + className;
    //Log(logmsg,"info");

    m_JSEngine.globalObject().setProperty(className, injectedObject);

    return injectedObject;
}

/**
 * @brief CJsManager::RegisterNewArray 注册一个新的数组
 * @param arraySize
 * @return
 */
QJSValue CJsManager::RegisterNewArray(int arraySize)
{
    return m_JSEngine.newArray(arraySize);
}

/**
 * @brief CJsManager::clean_system 卸载系统
 */
void CJsManager::clean_system(void)
{
    if(m_WebSocketServer.isListening())
        m_WebSocketServer.CloseServer();
}

/// 清除所有的插件
void CJsManager::cleanallplugin(void)
{
    QMap<QString,tagpluginItem*>::iterator iter = m_LibraryList.begin();
    for(;iter != m_LibraryList.end();++iter)
    {
        iter.value()->clear();
        delete iter.value();
    }

    m_LibraryList.clear();    
    m_ModuleList.clear();
}

/// 添加一个模块到系统中
void CJsManager::addModule(QString name,QString modulepath)
{
    if(name.isEmpty() ||
            modulepath.isEmpty() ||
            m_currentWorkingPath.isEmpty())
        return;

    QString pRealPath = modulepath;

    QFileInfo pFileInfo(pRealPath);
    if(!pFileInfo.exists())
    {
        pRealPath = m_currentWorkingPath+"/"+modulepath;
    }

    m_ModuleList[name] = m_JSEngine.importModule(pRealPath);
}

/// 调用指定的模块
QJSValue CJsManager::getModule(QString nudName)
{
    QMap<QString,QJSValue>::iterator iter = m_ModuleList.find(nudName);
    if(iter == m_ModuleList.end())
        return QJSValue(-1);

    QJSValue myFunction = iter.value().property(nudName);
    if(myFunction.isError())
        return QJSValue(-1);

    return myFunction;
}

/**
 * @brief CJsManager::SendLog 发送日志信息
 * @param msg 要发送的日志信息
 * @param type 日志类型
 * @param isuseGBK 是否使用GBK,从脚本过来的使用GBK
 * @param isPrint 是否打印
 * @param isSend 是否网络发送
 * @param isLog 是否本地记录
 * @param isProg 是否要显示代码信息，初始不显示
 */
void CJsManager::Log(QString msg, QString type,
                     bool isuseGBK,
                     bool isPrint,bool isSend,
                     bool isLog,bool isProg)
{
    if(msg.isEmpty() || type.isEmpty())
        return;

    QString tempMsg = msg;

    if(isuseGBK)
    {
        QTextCodec* pGBKCodec = QTextCodec::codecForName("UTF-8");
        tempMsg = QString::fromUtf8(pGBKCodec->fromUnicode(msg.simplified()));
    }

    QString content = QString::asprintf("%s:%d",
                                        //type.toStdString().c_str(),
                                        //QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss").toStdString().c_str(),
                                        __FILE__,
                                        __LINE__)+tempMsg;

    if(!isProg)
    {
        content = tempMsg;
    }

    if(isPrint)
        qDebug()<<content;

    if(isLog)
    {
        if(type == "info")
            QLOG_INFO()<<content;
        else if(type == "warn")
            QLOG_WARN()<<content;
        else if(type == "debug")
            QLOG_DEBUG()<<content;
        else if(type == "error")
            QLOG_ERROR()<<content;
        else if(type == "fatal")
            QLOG_FATAL()<<content;
    }

    emit on_signalLog(content,type);

    if(isSend)
    {
        QJsonObject mesObjReturn;
        mesObjReturn["mesid"] = IDD_JS_MESSAGE_LOG;
        mesObjReturn["deviceid"] = m_DeviceID;
        mesObjReturn["type"] = type;
        mesObjReturn["content"] = content;

        m_WebSocketServer.SendAll(JsonToString(mesObjReturn));
    }
}

void CJsManager::WorkingdirectoryChanged(void)
{
    if(!m_isAutoLoadScript ||
            m_currentWorkingPath.isEmpty()) return;

    emit on_signalreloadscript();

    Monitoringdirectory();

    if(js_loadscriptstring_callback(m_currentWorkingPath,(m_isDebugMode ? false : true)))
        js_setup_callback();

    emit on_signalreloadedandrunscript();
}

/**
 * @brief CJsManager::addOtherWorkingPath 添加其它要监控的目录
 * @param ppath
 */
void CJsManager::addOtherWorkingPath(QString ppath)
{
    if(ppath.isEmpty())
        return;

    //判断路径是否存在
    QDir dir(ppath);
    if(!dir.exists())
    {
        return;
    }

    m_pSystemWorkingPathWatcher.addPath(ppath);
    m_allResourcesPath.push_back(ppath);

    Monitoringdirectory();
}

/**
 * @brief CJsManager::setCurrentWorkingPath 设置当前系统工作目录
 * @param ppath 要设置的工作目录
 */
void CJsManager::setCurrentWorkingPath(QString ppath)
{
    if(ppath.isEmpty())
        return;

    //判断路径是否存在
    QDir dir(ppath);
    if(!dir.exists())
    {
        return;
    }

    m_currentWorkingPath = ppath;
    m_pSystemWorkingPathWatcher.addPath(ppath);
    m_allResourcesPath.push_back(ppath);

    Monitoringdirectory();
}

/**
 * @brief CJsManager::Monitoringdirectory 监控指定目录下所有的文件
 * @param directory
 */
void CJsManager::Monitoringdirectory(void)
{
    for(int i=0;i<m_allResourcesPath.size();i++)
    {
        QFileInfo pFileInfo(m_allResourcesPath[i]);
        if(!pFileInfo.exists())
            return;

        QVector<QString> pfilelist;
        if(FindFile(m_allResourcesPath[i],pfilelist) < 0)
            return;

        for(int i=0;i<pfilelist.size();i++)
            m_pSystemWorkingPathWatcher.addPath(pfilelist[i]);
    }
}

/**
 * @brief CJsManager::OnProcessConnectedNetMes 处理一个新的连接到达
 * @param conn 到达的新的连接
 */
void CJsManager::OnProcessConnectedNetMes(QWebSocket *conn)
{
    emit on_signalConnectedNetMes(conn);

    // 连接建立成功后，注册设备
    RegisterDeviceToServer();

    // 当连上服务器后，报告设备信息
    QString sendparams = QString::fromUtf8("%1:%2 %3 %4 %5 %6 %7 %8")
                            .arg(m_DeviceName)
                            .arg(m_DeviceID)
                            .arg(m_UserID)
                            .arg(m_UserPassword)
                            .arg(m_DataVersion)
                            .arg(m_isAutoLoadScript)
                            .arg(m_currentWorkingPath)
                            .arg(m_currentRegisterDllPath);
    Log(sendparams,"info",false);
}

/**
 * @brief CJsManager::OnProcessDisconnectedNetMes 处理一个连接关闭
 * @param conn 要断开的连接
 */
void CJsManager::OnProcessDisconnectedNetMes(QWebSocket *conn)
{
    emit on_signalDisconnectedNetMes(conn);
}

/// 设置反向代理服务器参数
/*void CJsManager::setReverseProxyServerParams(QString ipaddress,int port)
{
    m_ReverseProxyServerIP = QHostAddress(ipaddress);
    m_ReverseProxyServerPort = port;
}*/

/// 处理二进制文件发送，接收进度，type:0发送，1接收
void CJsManager::OnProcessBinaryOperProcess(QWebSocket *conn,int type,qint64 sendsize,qint64 totalsize)
{
    emit on_signalOperProcess(type,sendsize,totalsize);
}

/**
 * @brief CJsManager::getFileMd5 得到相应文件的MD5
 * @param path 要得到MD5码的文件夹
 * @return 返回文件夹的MD5ma码
 */
QString CJsManager::getFileMd5(QByteArray filedata)
{
    if(filedata.isEmpty())
        return QString();

    QCryptographicHash md5Hash(QCryptographicHash::Md5);
    md5Hash.addData(filedata,filedata.size());

    return md5Hash.result().toHex();
}

/**
 * @brief CLuaManager::OnProcessNetBinary 处理网络二进制消息
 * @param conn 要处理的客户端
 * @param data 到达的二进制消息
 */
void CJsManager::OnProcessNetBinary(QWebSocket *conn,QByteArray &data)
{
    emit on_signalNetBinary(conn,data);

    tagVersionOper pVersionOper;
    QByteArray reciverData = data;

    memset(&pVersionOper,0,sizeof(tagVersionOper));
    memcpy(&pVersionOper,reciverData.data(),sizeof(tagVersionOper));
    reciverData.remove(0,sizeof(tagVersionOper));

    switch(pVersionOper.tagIndex)
    {
    case IDD_JS_MESSAGE_PUTRESOURCEDATA:
    {
        bool isOk = Convertpackagetofolder(reciverData,m_currentWorkingPath);

        if(isOk) m_DataVersion = pVersionOper.subid;

        QJsonObject mesObj;
        mesObj["mesid"] = IDD_JS_MESSAGE_PUTRESOURCEDATA;
        mesObj["deviceid"] = pVersionOper.mainid;
        mesObj["version"] = pVersionOper.subid;
        mesObj["state"] = isOk ? IDD_JS_MESSAGE_SUCCESS : IDD_JS_MESSAGE_FAIL;

        m_WebSocketServer.Send(conn,JsonToString(mesObj));

        if(isOk && m_isAutoLoadScript)
        {
            // 导入脚本文件
            if(js_loadscriptstring_callback(m_currentWorkingPath))
                js_setup_callback();
        }
    }
        break;
    default:
        break;
    }
}

/**
 * @brief CJsManager::OnProcessNetText 处理网络字符串消息
 * @param conn 要处理的客户端
 * @param mes 到达的字符串消息
 */
void CJsManager::OnProcessNetText(QWebSocket *conn,QString mes)
{
    emit on_signalNetText(conn,mes);

    QJsonObject mesObj = StringToJson(mes);

    switch (mesObj["mesid"].toInt())
    {
    case IDD_JS_MESSAGE_GETRESOURCEDATA:            // 得到指定资源的数据
    {
        onProcessNetProjectGetData(conn,mesObj);
    }
        break;
    case IDD_JS_MESSAGE_REGISTER:                 // 设备注册
    {
        onProcessNetProjectRegisterDevice(conn,mesObj);
    }
        break;
    case IDD_JS_MESSAGE_COMMAND:                  // 用户命令
    {
        onProcessNetProjectUserCommand(conn,mesObj);
    }
        break;
    case IDD_EMS_MESSAGE_P2P_DEVICE:              // 和设备建立P2P连接
    {
        onProcessNetP2pDevice(conn,mesObj);
    }
        break;
    default:
        break;
    }
}

/// 开始执行脚本
void CJsManager::runScript(void)
{
    if(js_loadscriptstring_callback(m_currentWorkingPath))
        js_setup_callback();
}

/**
 * @brief CJsManager::js_loadscriptstring_callback 从指定文件中导入脚本并执行
 * @param scriptfilepath 要导入的脚本文件路径
 */
bool CJsManager::js_loadscriptstring_callback(QString scriptfilepath,bool isCheckMd5)
{
    if(scriptfilepath.isEmpty())
        return false;

    QString tmpscriptfilepath = scriptfilepath;

    if(tmpscriptfilepath.isEmpty())
        tmpscriptfilepath = m_currentWorkingPath;

    QVector<QString> pfilelist;
    if(FindFile(tmpscriptfilepath,pfilelist) < 0)
        return false;

    QString contents;

    for(int i=0;i<pfilelist.size();i++)
    {
        QFileInfo pFileInfo(pfilelist[i]);
        if(!pFileInfo.exists() || pFileInfo.suffix().toLower() != "js")
            continue;

        QFile scriptFile(pfilelist[i]);
        if (!scriptFile.open(QIODevice::ReadOnly))
        {
            QString logmsg = scriptfilepath + QString::fromLocal8Bit(" 导入失败.");
            Log(logmsg,"error",false);
            continue;
        }

        QTextStream stream(&scriptFile);
        contents += stream.readAll();
        scriptFile.close();
    }

    QString pcurrentScriptMd5 = getFileMd5(QByteArray(contents.toLatin1()));

    // 如果文件没有什么改变，就不加载了
    if(isCheckMd5 &&
            m_currentScriptMd5 == pcurrentScriptMd5)
    {
        Log(QString::asprintf("js_loadscriptstring_callback failed:file same.(isCheckMd5:%d)",(int)isCheckMd5),
            "info",
            false);
        return false;
    }

    m_currentScriptMd5 = pcurrentScriptMd5;

    QJSValue errorValue = m_JSEngine.evaluate(contents, scriptfilepath);
    if (errorValue.isError())
    {
        QString logmsg = "js_loadscriptstring_callback error:";
        logmsg += errorValue.property("name").toString() + ", "
                 + errorValue.property("message").toString() + ", "
                 //+ errorValue.property("lineNumber").toInt() + ", "
                 + errorValue.property("stack").toString();
        Log(logmsg,"error",false);

        return false;
    }
    else
    {
        Log("js_loadscriptstring_callback successed.","info",false);
    }

    return true;
}

/**
 * @brief CJsManager::execute_function_callback 执行指定名称的函数
 * @param functionName
 * @param params
 * @return
 */
bool CJsManager::execute_function_callback(QString functionName,QJSValueList params)
{
    if(functionName.isEmpty()) return false;

    QJSValue run_fun = m_JSEngine.globalObject().property(functionName);
    if(run_fun.isNull() || !run_fun.isCallable())
        return false;

    QJSValue errorValue = run_fun.call(params);
    if(errorValue.isError())
    {
        QString logmsg = "execute_function_callback error:";
        logmsg += errorValue.property("name").toString() + ", "
                 + errorValue.property("message").toString() + ", "
                 //+ errorValue.property("lineNumber").toInt() + ", "
                 + errorValue.property("stack").toString();
        Log(logmsg,"error",false);
    }
}

/**
 * @brief CJsManager::js_reset_callback 重置脚本系统
 */
void CJsManager::js_reset_callback(void)
{
    QJSValue run_fun = m_JSEngine.globalObject().property("resetsys");
    if(run_fun.isNull())
        return;

    QJSValue errorValue = run_fun.call();
    if(errorValue.isError())
    {
        QString logmsg = "js_reset_callback error:";
        logmsg += errorValue.property("name").toString() + ", "
                 + errorValue.property("message").toString() + ", "
                 //+ errorValue.property("lineNumber").toInt() + ", "
                 + errorValue.property("stack").toString();
        Log(logmsg,"error",false);
    }
}

/**
 * @brief CJsManager::js_setup_callback 初始脚本系统
 */
void CJsManager::js_setup_callback(void)
{
    emit on_signalsystemsetup();

    QJSValue run_fun = m_JSEngine.globalObject().property("setup");
    if(run_fun.isNull())
        return;

    QJSValue errorValue = run_fun.call();
    if(errorValue.isError())
    {
        QString logmsg = "js_setup_callback error:";
        logmsg += errorValue.property("name").toString() + ", "
                 + errorValue.property("message").toString() + ", "
                 //+ errorValue.property("lineNumber").toInt() + ", "
                 + errorValue.property("stack").toString();
        Log(logmsg,"error",false);
    }
}

/**
 * @brief CJsManager::reg_all2jss 注册所有用到的函数
 */
void CJsManager::reg_all2jss(void)
{
    if(m_currentRegisterDllPath.isEmpty())
        return;

    QVector<QString> pfilelist;
    if(FindFile(m_currentRegisterDllPath,pfilelist) < 0)
        return;

    for(int i=0;i<pfilelist.size();i++)
    {
        QFileInfo pFileInfo(pfilelist[i]);
        if(!pFileInfo.exists())
            continue;

#if defined (WIN32)
        if(pFileInfo.suffix().toLower() != "dll")
            continue;
#else
        if(pFileInfo.suffix().toLower() != "so")
            continue;
#endif

        QLibrary *pDllLibrary = new QLibrary(pfilelist[i]);
        if(!pDllLibrary->load())
        {
            QLOG_ERROR() << "load dll:"<<pfilelist[i]<<" fail.";
            Log("load dll fail.","error",false);

            continue;
        }

        reg_fun reg_xxx2Jss = (reg_fun)pDllLibrary->resolve("reg_xxx2Jss");
        if(reg_xxx2Jss)
        {
            tagpluginItem *ppluginItem = reg_xxx2Jss(this);
            if(ppluginItem)
            {
                ppluginItem->lib = pDllLibrary;
                m_LibraryList[ppluginItem->name] = ppluginItem;
                Log("load plugin:"+ppluginItem->name+" successed.","info",false);
            }
        }

        //pDllLibrary.unload();
    }
}

/// 得到指定名称的插件
tagpluginItem* CJsManager::getPluginByName(QString name)
{
    if(name.isEmpty())
        return NULL;

    QMap<QString,tagpluginItem*>::iterator iter = m_LibraryList.find(name);
    if(iter == m_LibraryList.end())
        return NULL;

    return iter.value();
}

/**
 * @brief CJsManager::Convertpackagetofolder 将指定的包数据转换到指定的文件夹
 * @param packageData 要转换的包数据
 * @param filePath 目标目录
 * @return 如果转换成功返回真，否则返回假
 */
bool CJsManager::Convertpackagetofolder(QByteArray packageData,QString filePath)
{
    if(packageData.isEmpty() || filePath.isEmpty())
        return false;

    QByteArray tempData = packageData;

    tagSVNVersionPathStruct ptagSVNVersionPathStruct;
    memset(&ptagSVNVersionPathStruct,0,sizeof(tagSVNVersionPathStruct));

    memcpy(&ptagSVNVersionPathStruct,tempData.data(),sizeof(tagSVNVersionPathStruct));
    tempData.remove(0,sizeof(tagSVNVersionPathStruct));

    if(ptagSVNVersionPathStruct.tag[0] != 'S' ||
       ptagSVNVersionPathStruct.tag[1] != 'V' ||
       ptagSVNVersionPathStruct.tag[2] != 'N')
        return false;

    qDebug()<<"Convertpackagetofolder1:"<<ptagSVNVersionPathStruct.srcDataSize;

    unsigned char *uncompressed_buffer = (unsigned char*)allocBytes(ptagSVNVersionPathStruct.srcDataSize);
    memset(uncompressed_buffer,0,ptagSVNVersionPathStruct.srcDataSize);
    fastlz_decompress(tempData.data(),tempData.size(),uncompressed_buffer,ptagSVNVersionPathStruct.srcDataSize);

    tempData = QByteArray((const char*)uncompressed_buffer,ptagSVNVersionPathStruct.srcDataSize);

    qDebug()<<"Convertpackagetofolder2:"<<tempData.size();

    deallocBytes(uncompressed_buffer);
    uncompressed_buffer=NULL;

    for(int i=0;i<ptagSVNVersionPathStruct.fileCount;i++)
    {
        tagSVNVersionFileStruct ptagSVNVersionFileStruct;
        memset(&ptagSVNVersionFileStruct,0,sizeof(ptagSVNVersionFileStruct));

        memcpy(&ptagSVNVersionFileStruct,tempData.data(),sizeof(tagSVNVersionFileStruct));
        tempData.remove(0,sizeof(tagSVNVersionFileStruct));

        QString pfileName = QString(ptagSVNVersionFileStruct.fileName);
        QString ptmpFilePath = filePath + pfileName;

        QString tmpFileDirPath = ptmpFilePath.mid(0,ptmpFilePath.lastIndexOf(tr("/")));

        QDir dir(tmpFileDirPath);
        if(!dir.exists())
        {
            if(!dir.mkpath(tmpFileDirPath))
            {
                return false;
            }
        }

        QFile precvFile(ptmpFilePath);
        if(precvFile.open(QIODevice::WriteOnly))
        {
            QByteArray tmpFileData = tempData.mid(0,ptagSVNVersionFileStruct.fileSize);
            tempData.remove(0,ptagSVNVersionFileStruct.fileSize);

            precvFile.write(tmpFileData);
            precvFile.close();
        }

        QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
    }

    return true;
}

/**
 * @brief CJsManager::Convertfoldertopackage 将指定文件夹转换成一个包数据
 * @param filePath 要转换的包
 * @return 返回转换后的包数据
 */
QByteArray CJsManager::Convertfoldertopackage(QString filePath)
{
    QByteArray returnData;

    if(filePath.isEmpty())
        return returnData;

    QVector<QString> pfilelist;

    int filecount = FindFile(filePath,pfilelist);
    if(filecount < 0)
        return returnData;

    QByteArray filesData;

    for(int i=0;i<pfilelist.size();i++)
    {
        QFile m_sendfile;
        m_sendfile.setFileName(pfilelist[i]);

        if(m_sendfile.open(QIODevice::ReadOnly))
        {
            QByteArray fileData = m_sendfile.readAll();

            QString tmpRealFilePath = pfilelist[i].mid(filePath.size());

            tagSVNVersionFileStruct ptagSVNVersionFileStruct;

            strncpy(ptagSVNVersionFileStruct.fileName,tmpRealFilePath.toUtf8().data(),MAX_FILENAME);
            ptagSVNVersionFileStruct.fileSize = fileData.size();

            filesData.append((const char*)&ptagSVNVersionFileStruct,sizeof(ptagSVNVersionFileStruct));
            filesData.append(fileData);

            m_sendfile.close();
        }

        OnProcessBinaryOperProcess(NULL,3,i+1,pfilelist.size());

        QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
    }

    tagSVNVersionPathStruct ptagSVNVersionPathStruct;
    ptagSVNVersionPathStruct.tag[0] = 'S';
    ptagSVNVersionPathStruct.tag[1] = 'V';
    ptagSVNVersionPathStruct.tag[2] = 'N';
    ptagSVNVersionPathStruct.fileCount = pfilelist.size();
    ptagSVNVersionPathStruct.srcDataSize = filesData.size();

    returnData.append((const char*)&ptagSVNVersionPathStruct,sizeof(ptagSVNVersionPathStruct));
    //returnData.append(filesData);

    //qDebug()<<"Convertfoldertopackage1:"<<filesData.size();

    unsigned char *compressed_buffer = (unsigned char*)allocBytes(filesData.size());
    memset(compressed_buffer,0,filesData.size());
    int compressedsize = fastlz_compress(filesData.data(),filesData.size(),compressed_buffer);
   // int compressedsize = fastlz_compress_level(2, filesData.data(),filesData.size(), compressed_buffer);

    //qDebug()<<"Convertfoldertopackage2:"<<compressedsize;

    returnData.append(QByteArray((const char*)compressed_buffer,compressedsize));

    deallocBytes(compressed_buffer);
    compressed_buffer=NULL;

    return returnData;
    //return qCompress(returnData);
}

/// 注册设备到设备服务器上
void CJsManager::RegisterDeviceToServer(void)
{
    if(m_DeviceID <= 0 ||
            m_UserID <= 0 ||
            m_UserPassword.isEmpty() ||
            !m_WebSocketServer.isListening())
        return;

    QJsonObject mesObj;
    mesObj["mesid"] = IDD_JS_MESSAGE_REGISTER;
    mesObj["deviceid"] = m_DeviceID;
    mesObj["myid"] = m_UserID;
    mesObj["mypwd"] = m_UserPassword;

    m_WebSocketServer.SendAll(JsonToString(mesObj));
}

void CJsManager::onProcessNetP2pDevice(QWebSocket *conn,QJsonObject &mesObj)
{
    int pdeviceId = mesObj["deviceid"].toInt();
    int pUserId = mesObj["myid"].toInt();

    QJsonObject mesObjReturn;
    mesObjReturn["deviceid"] = pdeviceId;
    mesObjReturn["userid"] = pUserId;

    QByteArray sendData = JsonToString(mesObjReturn).toUtf8();

    m_ReverseProxyServer.writeDatagram(sendData,
                                       m_ReverseProxyServerIP,
                                       m_ReverseProxyServerPort);
}

/// 用户命令处理
void CJsManager::onProcessNetProjectUserCommand(QWebSocket *conn,QJsonObject &mesObj)
{
    int mesType = mesObj["type"].toInt();

    switch(mesType)
    {
    case IDD_JS_MESSAGE_COMMAND_RELOAD:       // 重新导入脚本
    {
        // 导入脚本文件
        if(js_loadscriptstring_callback(m_currentWorkingPath,false))
            js_setup_callback();
    }
        break;
    case IDD_JS_MESSAGE_COMMAND_AUTOLOAD:     // 自动导入脚本
    {
        m_isAutoLoadScript = mesObj["autoload"].toInt() > 0 ? true : false;
    }
        break;
    default:
        break;
    }
}

/// 注册设备
void CJsManager::onProcessNetProjectRegisterDevice(QWebSocket *conn,QJsonObject &mesObj)
{
    int state = mesObj["state"].toInt();
    QString udpserverip = mesObj["udpserip"].toString();
    int udpserverport = mesObj["udpserport"].toInt();

    if(state == IDD_JS_MESSAGE_FAIL)
    {
        m_WebSocketServer.CloseServer();
    }
    else if(state == IDD_JS_MESSAGE_SUCCESS)
    {
        m_ReverseProxyServerIP = QHostAddress(udpserverip);
        m_ReverseProxyServerPort = udpserverport;
    }
}

/**
 * @brief CJsManager::onProcessNetProjectGetInfoContent 得到指定资源的数据
 * @param conn
 * @param mesObj
 */
void CJsManager::onProcessNetProjectGetData(QWebSocket *conn,QJsonObject &mesObj)
{
    int deviceid = mesObj["deviceid"].toInt();
    int userid = mesObj["userid"].toInt();
    QString userpwd = mesObj["userpwd"].toString();

    if(m_currentWorkingPath.isEmpty() ||
            m_DeviceID != deviceid ||
            m_UserID != userid ||
            m_UserPassword != userpwd)
    {
        QJsonObject mesObjReturn;
        mesObjReturn["mesid"] = IDD_JS_MESSAGE_GETRESOURCEDATA;
        mesObjReturn["state"] = IDD_JS_MESSAGE_FAIL;
        m_WebSocketServer.Send(conn,JsonToString(mesObjReturn));
        return;
    }

    tagVersionOper pVersionOper;
    memset(&pVersionOper,0,sizeof(tagVersionOper));

    pVersionOper.tagIndex = IDD_JS_MESSAGE_GETRESOURCEDATA;

    QByteArray sendData;
    sendData.append((const char*)&pVersionOper,sizeof(tagVersionOper));
    sendData.append(Convertfoldertopackage(m_currentWorkingPath));

    m_WebSocketServer.SendByteArray(conn,sendData,true);
}

/// 向控制设备发送数据
bool CJsManager::sendDataToControlDevice(QByteArray data,bool isprocessEvents)
{
    if(data.isEmpty())
        return false;

    qint64 m_sendsize,m_totaldatasize;

    QByteArray tmpByteArray = qCompress(data);

    m_totaldatasize = tmpByteArray.size();
    m_sendsize=0;
    quint16 pdecchecknum = qChecksum(tmpByteArray.constData(),tmpByteArray.size());

    tagDatagramHearder ptagDataStruct;
    memset(&ptagDataStruct,0,sizeof(ptagDataStruct));

    ptagDataStruct.version = 100;

    ptagDataStruct.compressdataSize = tmpByteArray.size();
    ptagDataStruct.srcdataSize = m_totaldatasize;
    ptagDataStruct.checknum = pdecchecknum;

    QByteArray pHeardBytes = QByteArray((char*)&ptagDataStruct,sizeof(ptagDataStruct));

    if(m_ReverseProxyServer.writeDatagram(pHeardBytes,
                                          m_ControlDeviceIP,
                                          m_ControlDevicePort) != pHeardBytes.size())
    {
        return false;
    }

    do
    {
        QByteArray psendbytearray;

        if(tmpByteArray.size()-m_sendsize < BUF_SIZE)
            psendbytearray = tmpByteArray.mid(m_sendsize);
        else
            psendbytearray = tmpByteArray.mid(m_sendsize,BUF_SIZE);

        m_sendsize += m_ReverseProxyServer.writeDatagram(psendbytearray,
                                                         m_ControlDeviceIP,
                                                         m_ControlDevicePort);

        // 传输进度
        emit on_signalProcessBinaryOperProcess(0,m_sendsize,m_totaldatasize);

        if(isprocessEvents)
            QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
    }
    while(m_sendsize < m_totaldatasize);

    return true;
}

void CJsManager::ReverseProxyServer_reciverPendingDatagram()
{
    while(m_ReverseProxyServer.hasPendingDatagrams())
    {
        QByteArray m_reciverDatagramData;                 /**< 用于接收数据 */
        m_reciverDatagramData.resize(m_ReverseProxyServer.pendingDatagramSize());

        if(m_ReverseProxyServer.readDatagram(m_reciverDatagramData.data(),m_reciverDatagramData.size(),
                                    &m_ControlDeviceIP, &m_ControlDevicePort) > 0)
        {
            m_recvDataBytes.push_back(m_reciverDatagramData);

            while(!m_recvDataBytes.isEmpty())
            {
                if(!m_recvDataState &&
                    m_recvDataBytes.size() >= sizeof(tagDatagramHearder))
                {
                    memcpy(&m_tagDataStruct,m_recvDataBytes.constData(),sizeof(m_tagDataStruct));

                    if(m_tagDataStruct.version != 100)
                    {
                        m_recvDataState = false;
                        m_recvDataBytes.clear();
                        break;
                    }

                    // 下载进度
                    emit on_signalProcessBinaryOperProcess(1,m_recvDataBytes.size(),m_tagDataStruct.compressdataSize);

                    m_recvDataBytes.remove(0,sizeof(m_tagDataStruct));
                    m_recvDataState = true;
                }

                // 下载进度
                if(m_recvDataBytes.size() > 0)
                    emit on_signalProcessBinaryOperProcess(1,m_recvDataBytes.size() < m_tagDataStruct.compressdataSize ? m_recvDataBytes.size() : m_tagDataStruct.compressdataSize,m_tagDataStruct.compressdataSize);

                if(!m_recvDataState || m_recvDataBytes.size() < m_tagDataStruct.compressdataSize)
                    break;

                // 下载进度
                emit on_signalProcessBinaryOperProcess(1,m_tagDataStruct.compressdataSize,m_tagDataStruct.compressdataSize);

                // 得到当前数据
                QByteArray precvDataData = m_recvDataBytes.mid(0,m_tagDataStruct.compressdataSize);
                m_recvDataBytes.remove(0,m_tagDataStruct.compressdataSize);

                // 获取文件校验码
                quint16 pdecchecknum = qChecksum(precvDataData.constData(),precvDataData.size());

                if(m_tagDataStruct.checknum == pdecchecknum)
                {
                    // 解压文件
                    QByteArray precvDataBytes = qUncompress(precvDataData);

                    if(m_isEnableScreen)
                    {
                        if(m_SendScreenCount == -1)
                        {
                            m_TimerSendScreen.start(m_refreshfrequency);
                        }

                        m_SendScreenCount = 0;
                    }

                    if(precvDataBytes != "heart")
                    {
                        signalProcessControlDeviceNetBinary(precvDataBytes);

                        emit on_signalControlDeviceNetBinary(precvDataBytes);
                    }
                }

                m_recvDataState=false;
            }
        }
    }
}

/// 发送屏幕到控制设备上
void CJsManager::HandlerTimerSendScreen(void)
{
    if(m_SendScreenCount > 5)
    {
        m_TimerSendScreen.stop();
        m_SendScreenCount = -1;
        return;
    }

    QScreen *screen = QApplication::primaryScreen(); //获取当前程序的主窗口对象
    QPixmap scaleImage = screen->grabWindow(QApplication::desktop()->winId());

    QPainter imgpainter(&scaleImage);
    imgpainter.drawPixmap(QCursor::pos(),getMouseCursorIconWin());
    imgpainter.end();

    scaleImage = scaleImage.scaled(m_ScreenWidth,m_ScreenHeight,Qt::IgnoreAspectRatio,Qt::SmoothTransformation);

    QtConcurrent::run(this,&CJsManager::ConvertImageToBytes,scaleImage);

    m_SendScreenCount += 1;
}

void CJsManager::ConvertImageToBytes(const QPixmap& image)
{
    if(image.isNull())
        return;

    QByteArray imagebytes;
    QBuffer buffer(&imagebytes);
    buffer.open(QIODevice::WriteOnly);
    image.save(&buffer, "JPG"); // writes image into ba in PNG format

    emit signal_convertImageToBytes(imagebytes);
}

/// 发送屏幕数据到控制设备上
void CJsManager::signalProcessSendScreenToControlDevice(QByteArray data)
{
    sendDataToControlDevice(data);
}

QPixmap CJsManager::getMouseCursorIconWin()
{
#if defined (WIN32)
    // Get Cursor Size
    int cursorWidth = GetSystemMetrics(SM_CXCURSOR);
    int cursorHeight = GetSystemMetrics(SM_CYCURSOR);

    // Get your device contexts.
    HDC hdcScreen = GetDC(NULL);
    HDC hdcMem = CreateCompatibleDC(hdcScreen);

    // Create the bitmap to use as a canvas.
    HBITMAP hbmCanvas = CreateCompatibleBitmap(hdcScreen, cursorWidth, cursorHeight);

    // Select the bitmap into the device context.
    HGDIOBJ hbmOld = SelectObject(hdcMem, hbmCanvas);

    // Get information about the global cursor.
    CURSORINFO ci;
    ci.cbSize = sizeof(ci);
    GetCursorInfo(&ci);

    // Draw the cursor into the canvas.
    DrawIcon(hdcMem, 0, 0, ci.hCursor);

#endif

    // Convert to QPixmap
    QPixmap cursorPixmap;

#if defined (WIN32)
    cursorPixmap = QtWin::fromHBITMAP(hbmCanvas, QtWin::HBitmapAlpha);

    // Clean up after yourself.
    SelectObject(hdcMem, hbmOld);
    DeleteObject(hbmCanvas);
    DeleteDC(hdcMem);
    ReleaseDC(NULL, hdcScreen);
#endif

    return cursorPixmap;
}

/// 从控制设备接收到的数据
void CJsManager::signalProcessControlDeviceNetBinary(QByteArray data)
{
    QJsonObject mesObj = StringToJson(data);

    switch (mesObj["type"].toInt())
    {
    case QEvent::MouseButtonPress:
    case QEvent::MouseButtonRelease:
    case QEvent::MouseMove:
    {
        int pbutton = mesObj["button"].toInt();
        float mouseXRate = (float)mesObj["mouseX"].toInt() / (float)m_ScreenWidth;
        float mouseYRate = (float)mesObj["mouseY"].toInt() / (float)m_ScreenHeight;

        QRect mm = QGuiApplication::primaryScreen()->availableGeometry();

        float decX = mm.width() * mouseXRate;
        float decY = mm.height() * mouseYRate;

#if defined (WIN32)
        ::SetCursorPos((int)decX,(int)decY);

        if(mesObj["type"].toInt() == QEvent::MouseButtonPress)
        {
            if(pbutton == Qt::LeftButton)
                ::mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);
            else if(pbutton == Qt::RightButton)
                ::mouse_event(MOUSEEVENTF_RIGHTDOWN,0,0,0,0);
        }
        else if(mesObj["type"].toInt() == QEvent::MouseButtonRelease)
        {
            if(pbutton == Qt::LeftButton)
                ::mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);
            else if(pbutton == Qt::RightButton)
                ::mouse_event(MOUSEEVENTF_RIGHTUP,0,0,0,0);
        }
#endif

        /*qDebug()<<mesObj["mouseX"].toInt() << " "
                <<mesObj["mouseY"].toInt() << " "
                <<mm.width() << " "
                <<mm.height() << " "
                <<mouseXRate << " "
                <<mouseYRate << " "
                <<pbutton << " "
                <<decX << " "
                <<decY;*/
    }
        break;
    case QEvent::KeyPress:
    {
        int pkey = mesObj["key"].toInt();

#if defined (WIN32)
        ::keybd_event(pkey,0,0,0);
        ::keybd_event(pkey,0,KEYEVENTF_KEYUP,0);
#endif
    }
        break;
    default:
        break;
    }
}
